package com.guavamail.smtp.command.impl;

import java.io.IOException;

import org.apache.mina.core.session.IoSession;

import com.guavamail.smtp.SMTPContext;
import com.guavamail.smtp.SessionStatus;
import com.guavamail.smtp.command.AbstractCommand;
import com.guavamail.smtp.exception.LoginFailedException;

public class AUTH extends AbstractCommand {
	public static final String VERB = "AUTH";

	public static final String AUTH_CANCEL_COMMAND = "*";

	public AUTH() {
		super(VERB, "");
	}

	@Override
	public void execute(String commandString, IoSession ioSession, SMTPContext ctx) throws IOException {
		SessionStatus sessionStatus = ctx.getSessionStatus();
		if (sessionStatus.isAuthenticated()) {
			sendResponse(ioSession, "503 Refusing any other AUTH command");
			return;
		}

		boolean authenticating = sessionStatus.isAuthenticating();
		if (!authenticating) {
			String[] args = getArgs(commandString);

			// Let's check the command syntax
			if (args.length < 2) {
				sendResponse(ioSession, "501 Syntax: " + VERB + " mechanism [initial-response]");
				return;
			}

			// Let's check if we support the required authentication mechanism
			String mechanism = args[1];
			if (!ctx.getAuthenticationHandler().getAuthenticationMechanisms().contains(mechanism.toUpperCase())) {
				sendResponse(ioSession, "504 Unrecognized authentication type");
				return;
			}

			// OK, let's go trough the authentication process.
			// The authentication process may require a series of
			// challenge-responses
			if (authenticating && commandString.trim().equals(AUTH_CANCEL_COMMAND)) {
				// RFC 2554 explicitly states this:
				sendResponse(ioSession, "501 Authentication canceled by client");
				return;
			}

			try {
				StringBuilder response = new StringBuilder();
				boolean finished = ctx.getAuthenticationHandler().auth(commandString, response, ctx);

				sessionStatus.setAuthenticating(!finished);

				if (!finished) {
					// challenge-response iteration
					sendResponse(ioSession, response.toString());
					return;
				}

				sessionStatus.setAuthenticated(true);
				sendResponse(ioSession, "235 Authentication successful");
			} catch (LoginFailedException e) {
				sendResponse(ioSession, "535 Authentication failure");
				sessionStatus.setAuthenticated(false);
				sessionStatus.setAuthenticating(false);
			}

		}
	}

}
